package run.siyuan.manag.aspect;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import run.siyuan.manag.annotation.OperLog;
import run.siyuan.manag.entity.SysLog;
import run.siyuan.manag.service.SysLogService;
import run.siyuan.manag.util.SecurityUtil;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Map;

/**
 * @author lyz
 * @title: OperationAspect
 * @projectName springcloud
 * @date 2020/9/23
 * @description: 操作日志切面处理类
 */
@Slf4j
@Aspect
@Component
public class OperationLogAspect {
    @Autowired
    SysLogService sysLogService;
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private static Long dataTime = null;

    /**
     * 设置操作日志切入点   在注解的位置切入代码
     */
    @Pointcut("@annotation(run.siyuan.manag.annotation.OperLog)")
    public void logPointCut() {
    }

    @Before(value = "logPointCut()")
    public void before() {
        dataTime = DateUtil.currentSeconds();
    }

    /**
     * 记录操作日志
     *
     * @param joinPoint 方法的执行点
     * @param result    方法返回值
     * @throws Throwable
     */
    @AfterReturning(returning = "result", value = "logPointCut()")
    public void afterReturning(JoinPoint joinPoint, Object result) throws Throwable {
        long seconds = DateUtil.currentSeconds();
        Long time = seconds - dataTime;
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        try {
            //将返回值转换成map集合
            String resultJson = JSONUtil.toJsonStr(result);
            SysLog sysLog = new SysLog();
            String username = SecurityUtil.getUsername();
            sysLog.setUserName(username);
            sysLog.setOperation(request.getMethod());
            sysLog.setUrl(request.getRequestURI());
            sysLog.setParams(JSONUtil.toJsonStr(request.getParameterMap()));
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            //获取切入点所在的方法
            Method method = signature.getMethod();
            String className = method.getDeclaringClass().getName();
            String methodName = method.getName();
            //获取操作
            OperLog operLog = method.getAnnotation(OperLog.class);
            if (operLog != null) {
                sysLog.setDescription(operLog.description());
            }
            sysLog.setMethod(className + "." + methodName);
            sysLog.setTime(time);
            sysLog.setIp(getIp(request));
            sysLog.setMethodResult(resultJson);
            log.info("操作日志:{}", sysLog);
            sysLogService.save(sysLog);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private String getIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}